﻿using System;
using System.Drawing;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sebarf.Diagnostics.Interfaces;

namespace Tests.Basics.UI {
	public partial class MotionDetectionFromCamera : Form {
		public MotionDetectionFromCamera() {
			InitializeComponent();

			try {
				var capturedImageBox = new ImageBox(); //create an image viewer
				capturedImageBox.Height = panel2.Height;
				capturedImageBox.Width = panel2.Width;
				var motionImageBox = new ImageBox(); //create an image viewer
				motionImageBox.Dock = DockStyle.Fill;
				panel2.Controls.Add(capturedImageBox);
				panel1.Controls.Add(motionImageBox);
				Capture capture = null;
				try {
					capture = new Capture(); //create a camera captue
				}
				catch (Exception) {
					Logger.WriteInformation("No camera found");
					return;
				}

				var motionHistory = new MotionHistory(6,
					//number of images to store in buffer, adjust it to fit your camera's frame rate
													   20,
					//0-255, the amount of pixel intensity change to consider it as motion pixel
													   1.0, //in second, the duration of motion history you wants to keep
													   0.05, //in second, parameter for cvCalcMotionGradient
													   0.5); //in second, parameter for cvCalcMotionGradient
				Application.Idle += delegate {
					using (var storage = new MemStorage())
					//create storage for motion components
				                    	{
						Image<Bgr, Byte> image = capture.QueryFrame();
						//image = image.Canny (new Bgr (140, 140, 140), new Bgr (200, 200, 200));
						//reduce noise from the image
						capturedImageBox.Image = image;

						//update the motion history
						motionHistory.Update(image.Convert<Gray, Byte>());

						#region get a copy of the motion mask and enhance its color

						Image<Gray, Byte> motionMask = motionHistory.Mask;
						double[] minValues, maxValues;
						Point[] minLoc, maxLoc;
						motionMask.MinMax(out minValues, out maxValues, out minLoc, out maxLoc);
						motionMask._Mul(255.0 / maxValues[0]);

						#endregion

						//create the motion image 
						var motionImage = new Image<Bgr, byte>(motionMask.Size);
						//display the motion pixels in blue (first channel)
						motionImage[0] = motionMask;

						//Threshold to define a motion area, reduce the value to detect smaller motion
						double minArea = 100;

						storage.Clear(); //clear the storage
						Seq<MCvConnectedComp> motionComponents = motionHistory.GetMotionComponents(storage);

						//iterate through each of the motion component
						foreach (MCvConnectedComp comp in motionComponents) {
							//reject the components that have small area;
							if (comp.area < minArea) {
								continue;
							}

							// find the angle and motion pixel count of the specific area
							double angle, motionPixelCount;
							motionHistory.MotionInfo(comp.rect, out angle, out motionPixelCount);

							//reject the area that contains too few motion
							if (motionPixelCount < comp.area * 0.05) {
								continue;
							}

							//Draw each individual motion in red
							DrawMotion(motionImage, comp.rect, angle, new Bgr(Color.Red));
						}

						// find and draw the overall motion angle
						double overallAngle, overallMotionPixelCount;
						motionHistory.MotionInfo(motionMask.ROI, out overallAngle, out overallMotionPixelCount);
						DrawMotion(motionImage, motionMask.ROI, overallAngle, new Bgr(Color.Green));

						//Display the amount of motions found on the current image
						//UpdateText(String.Format("Total Motions found: {0}; Motion Pixel count: {1}", motionComponents.Total, overallMotionPixelCount));

						//Display the image of the motion
						motionImageBox.Image = motionImage;
					}
				};
			}
			catch (Exception ex) {
				Logger.WriteError(ex.Message);
				Assert.Fail(ex.Message);
			}
		}

		private static void DrawMotion(Image<Bgr, Byte> image, Rectangle motionRegion, double angle, Bgr color) {
			float circleRadius = (motionRegion.Width + motionRegion.Height) >> 2;
			var center = new Point(motionRegion.X + motionRegion.Width >> 1, motionRegion.Y + motionRegion.Height >> 1);

			var circle = new CircleF(center, circleRadius);

			var xDirection = (int)(Math.Cos(angle * (Math.PI / 180.0)) * circleRadius);
			var yDirection = (int)(Math.Sin(angle * (Math.PI / 180.0)) * circleRadius);
			var pointOnCircle = new Point(center.X + xDirection, center.Y - yDirection);
			var line = new LineSegment2D(center, pointOnCircle);

			image.Draw(circle, color, 1);
			image.Draw(line, color, 2);
		}
	}
}